﻿using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using PhotoGallery.Models;
using PhotoGallery.Repositories;
using PhotoGallery.Utils;
using QCloud.CosApi.Api;
using QCloud.CosApi.Common;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace PhotoGallery.API
{
    /// <summary>
    /// 相册API
    /// </summary>
    public class PhotoGalleryController : Controller
    {
        private PhotoGalleryRepository _repo;
        private IHostingEnvironment _environment;
        private ImageProcessor _processor;
        private IConfiguration _config;
        private readonly ILogger _logger;
        private IMemoryCache _cache;
        internal const string _albumsCacheKey = "PhotoGallery_Albums_Cache_Key";
        //Token有效期
        private static TimeSpan _expiresSpan;
        private  int APP_ID = 1252912192;
        private string SECRET_ID = "AKID6B5qQoMDAvUs32jgssiEcoocFze9lklh";
        private string SECRET_KEY = "76yoV0JQhs4IPApJck3KTRHzXt32ub6S";       
        private string bucketName = "photogallery";
        private string remotePath = "/albums";
        /// <summary>
        /// 相册api构造函数
        /// </summary>
        /// <param name="photoGalleryRepo">数据库context</param>
        /// <param name="environment">环境变量</param>
        /// <param name="processor">图片处理器</param>
        /// <param name="config">网站配置</param>
        /// <param name="cache">缓存</param>
        /// <param name="logger">日志</param>
        /// <returns>只压缩未裁剪的图片路径</returns>
        public PhotoGalleryController(
            PhotoGalleryRepository photoGalleryRepo,
            IHostingEnvironment environment,
            ImageProcessor processor,
            IConfiguration config,
            IMemoryCache cache,
            ILoggerFactory logger)
        {
            _config = config;
            _repo = photoGalleryRepo;
            _environment = environment;
            _processor = processor;
            _cache = cache;
            _logger = logger.CreateLogger<PhotoGalleryController>();
            if (_expiresSpan == default(TimeSpan))
            {
                _expiresSpan = ServerHelper.GetTimeSpan(_config);                
            }
           
            int app_id = 0;
            int.TryParse(_config["CosCloud:APP_ID"], out app_id);
            APP_ID = app_id;
            SECRET_ID = _config["CosCloud:SECRET_ID"];
            SECRET_KEY = _config["CosCloud:SECRET_KEY"];
            bucketName = _config["CosCloud:BucketName"];
            remotePath = _config["CosCloud:RemotePath"];

        }

        /// <summary>
        /// 获取相册图片集
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <param name="albumName">相册名称，不传或者传all表示获取所有名称</param>
        /// <returns>
        /// 返回Json类型,如下
        /// [
        /// {
        ///   AlbumName = "照片所属相册名称",
        ///   PhotoName = "照片名称"
        ///   Link = "照片Url"
        /// }
        /// ]
        /// </returns>
        [Route("api/GetAlbums")]
        [HttpGet]
        public JsonResult GetAlbums(string userName,string albumName)
        {
            List<AlbumInfoModel> alist = null;
            
            _cache.TryGetValue(CacheKeys.AllAlbumsKey, out alist);
            if (alist != null && alist.Count >0)
            {
               
                if (string.IsNullOrEmpty(albumName) || "all".Equals(albumName, StringComparison.OrdinalIgnoreCase))
                {
                    var query = alist.Where(w => w.IsPublic);
                    if (!string.IsNullOrEmpty(userName))
                    {
                        query.Where(w => w.Owner == userName);
                    }
                    var list = query.Select(a => new AlbumsResult(a.Name, a.Photos)).ToList();
                    return Json(list);                    
                }
                else
                {
                    //从缓存中筛选
                    var query = alist.Where(w => w.IsPublic);
                    if (!string.IsNullOrEmpty(userName))
                    {
                        query.Where(w => w.Owner == userName);
                    }
                    var list = query.Where(w =>  w.Name == albumName)
                        .Select(a => new AlbumsResult(a.Name, a.Photos)).ToList();
                    return Json(list);
                }
               
            }
            List<AlbumsResult> photoList = null;
             alist = _repo.Albums.ToList();
            var photos = _repo.Photos.ToList();
            for(var i = 0; i < alist.Count; i++)
            {
                alist[i].Photos = photos.Where(w => w.AlbumName == alist[i].Name).ToList();
            }
            _cache.Set(CacheKeys.AllAlbumsKey,alist);
            if(string.IsNullOrEmpty(albumName) || "all".Equals(albumName, StringComparison.OrdinalIgnoreCase))
            {
                var query = alist.Where(w => w.IsPublic);
                if (!string.IsNullOrEmpty(userName))
                {
                    query.Where(w => w.Owner == userName);
                }
                photoList = query.Select(o=> new AlbumsResult(o.Name,o.Photos)).ToList();
            }
            else
            {
                var query = alist.Where(w => w.IsPublic);
                if (!string.IsNullOrEmpty(userName))
                {
                    query.Where(w => w.Owner == userName);
                }
                photoList = query.Where(w=>w.Name == albumName).Select(o => new AlbumsResult(o.Name, o.Photos)).ToList();
            }
            
            return Json(photoList);
        }

        /// <summary>
        /// 获取名称名称集合
        /// </summary>
        /// <returns></returns>
        [Route("api/GetAlbumsName")]
        [HttpGet]
        public JsonResult GetAlbumsName()
        {
            List<AlbumInfoModel> alist = null;

            _cache.TryGetValue(CacheKeys.AllAlbumsKey, out alist);
            if (alist == null || alist.Count < 1)
            {
                var photos = _repo.Photos.ToList();

                alist = _repo.Albums.ToList();
                for(var i = 0; i < alist.Count; i++)
                {
                    alist[i].Photos = photos.Where(w => w.AlbumName == alist[i].Name).ToList();
                }
                if (alist.Count < 1)
                {
                    return Json(new FailResult(1005, "云相册名称为空，请先创建"));
                }
                _cache.Set(CacheKeys.AllAlbumsKey, alist);
               var model = alist.Select(o => new AlbumsNameResult(o.Name, o.Photos.Count)).ToList();
                return Json(model);
            }
            else
            {
                var model = alist.Select(o => new AlbumsNameResult(o.Name, o.Photos.Count)).ToList();
                return Json(model);
            }            
        }
        /// <summary>
        /// 获取腾讯云存储cos认证令牌,有效期600秒
        /// </summary>
        /// <param name="bucketName">腾讯云存储bucketName</param>
        /// <returns></returns>
        [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
        [Route("api/GenerateCosSignature")]
        [HttpPost]
        public JsonResult GenerateCosSignature(string bucketName)
        {
            try
            {
                var signTime = 600;
                var expired = DateTime.Now.ToUnixTime() / 1000 + signTime;
                var sign = Sign.Signature(APP_ID, SECRET_ID, SECRET_KEY, expired, bucketName);
                var ret = new SignatureResult(sign, DateTime.Now.AddMilliseconds(signTime));
                return Json(ret);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "生成cos令牌失败");
                return Json(new SignatureResult("", DateTime.Now) { Code = 500, Msg = ex.Message });
            }
            
        }

        /// <summary>
        /// 文件上传api
        /// </summary>
        /// <param name="albumName">相册名称</param>
        /// <param name="userName">用户名，如果传空则为上传到公共空间</param>
        /// <param name="files">文件</param>
        /// <param name="isPublic">是否公开</param>
        /// <returns>
        /// 返回Json数据,例:
        /// 错误返回:
        /// {
        ///   Code = "1" //0表示成功,其他表示失败或者错误; 1001:表示没有传入文件，1002：没传入相册名称
        ///   Msg = "用户不存在"
        /// }
        /// 正确返回:
        /// {
        ///     Code = "0"
        ///     Msg = "OK"
        /// }
        /// </returns>
        [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
        [Route("api/FileUpload")]
        [HttpPost]
        public async Task<IActionResult> FileUpload(ICollection<IFormFile> files,string albumName, string userName = "",bool isPublic = true)
        {
            
            int nSucceedFile = 0;
            try
            {
                int clientType = 1;
               
                if (files == null || files.Count < 1)
                {
                    var formFile = Request.Form.Files;
                    if(formFile == null || formFile.Count < 1)
                    {
                        return Json(new FailResult(1001, "请提供上传文件"));

                    }
                    else
                    {
                        files = formFile.ToList();
                    }
                }
                if (String.IsNullOrEmpty(albumName))
                {
                    return Json(new FailResult(1002, "请指定相册名称"));
                }
                var userEmpty = false;
                var strAlbumType = "public";
                IQueryable<AlbumInfoModel> query = null;
                if (string.IsNullOrEmpty(userName))
                {
                     query = _repo.Albums.Where(a => a.Name == albumName && a.IsPublic && a.Owner == string.Empty);
                    userEmpty = true;
                    strAlbumType = "public";
                }
                else
                {
                    query = _repo.Albums.Where(a => a.Name == albumName && a.Owner == userName && a.IsPublic == isPublic);
                    userEmpty = false;
                    strAlbumType = "user";
                }

                var album = query.FirstOrDefault();
                string albumPath = "";
               
                if (album == null) //不存在的相册则进行创建
                {                    
                    if (userEmpty) //公共目录
                    {
                        albumPath = Path.Combine(_environment.WebRootPath, "albums",strAlbumType,albumName);
                    }
                    else
                    {
                        albumPath = Path.Combine(_environment.WebRootPath, "albums", strAlbumType,userName, albumName);
                    }

                                       
                    var alumId = Guid.NewGuid().ToString();
                    album = new AlbumInfoModel() { Id = alumId, AlbumPath = albumPath, Name = albumName, Owner = userName, CreateDate = DateTime.Now, Description = albumName, IsPublic = isPublic, AlbumType = userEmpty ? 0 : 1 };
                    //_ac.Albums.Add(album);
                    //return Json(new FailResult(404, "相册名称不存在"));
                    _repo.Albums.Add(album);
                }
                else
                {
                    albumPath = album.AlbumPath;
                }

                if (!Directory.Exists(albumPath))
                {
                    Directory.CreateDirectory(albumPath);
                }
                List<PhotoInfoModel> photos = new List<PhotoInfoModel>();
                var cos = new CosCloud(APP_ID, SECRET_ID, SECRET_KEY, 60);
                StringBuilder strBuf = new StringBuilder();
                foreach (var file in files.Where(f => Utils.ServerHelper.IsImageFile(f.FileName)))
                {

                    strBuf.AppendLine(file.FileName);
                    var fileHash = "";
                    string photoName = file.FileName;
                    Console.WriteLine(photoName);
                    string photoExt = Path.GetExtension(photoName);
                    using (var imageStream = file.OpenReadStream())
                    {
                        fileHash = Utils.ServerHelper.MakeSHA1(imageStream);
                    }
                    if (string.IsNullOrEmpty(fileHash))
                    {
                        return Json(new FailResult(500, "无法获取文件hash"));
                    }
                    string path = Path.Combine(albumPath, photoName);
                    
                    if (System.IO.File.Exists(path))
                    {
                        //path = Path.ChangeExtension(path, file.GetHashCode() + Path.GetExtension(path));
                        continue;
                    }
                    
                    using (var imageStream = file.OpenReadStream())
                    {
                        using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write))
                        {
                            _processor.CreateThumbnails(imageStream, path,fileHash);
                            await file.CopyToAsync(fileStream);
                        }
                    }
                    //上传文件到云存储
                    string uploadPhoto = Path.Combine(Path.GetDirectoryName(path), "thumbnail",$"{fileHash}{photoExt}");

                    var strRet = await UploadFile(cos,uploadPhoto);
                    var uploadRet = JsonConvert.DeserializeObject<FileUploadResult>(strRet);
                    var photo = new PhotoInfoModel()
                    {
                        AlbumName = albumName,
                        Description = photoName,
                        Hash = fileHash,
                        LocalPath = path,
                        Name = photoName,
                        UploadDate = DateTime.Now,
                        UploadUser = userName,
                        UploadClient = clientType,
                        Path = path
                    };
                    if(uploadRet != null && uploadRet.code == 0)
                    {
                        photo.Path = uploadRet.data.resource_path;
                        photo.CDNUrl = uploadRet.data.access_url;
                        photo.CDNUrl2 = uploadRet.data.source_url;
                    }
                    //album.Photos.Insert(0, photo);
                    photos.Add(photo);
                    ++nSucceedFile;
                }
                _logger.LogInformation("IP:{0}上传文件到[{1}]目录,数量:{2},成功", ServerHelper.GetIP(HttpContext),albumName,files.Count);
                album.CoverPhoto = photos.FirstOrDefault()?.CDNUrl;
                try
                {
                    
                    _repo.Photos.AddRange(photos);

                    var nRet = await _repo.SaveChangesAsync();
                    Console.WriteLine("写入成功记录数:" + nRet.ToString());
                    //删除缓存
                    _cache.Remove(CacheKeys.AllAlbumsKey);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "上传写数据库失败，user:{0},albums{1},photos:[2]", userName, albumName, strBuf.ToString());
                }
                //写数据库

                
                //_cache.Remove(_albumsCacheKey);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "上传文件失败,名称名称:{0},错误信息:{1}", albumName, ex.Message);
                return Json(new FailResult(500, ex.Message));
            }

            return Json(new SucceedResult() { Msg = "成功上传图片："+nSucceedFile});
            
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="cos">CosCloud对象</param>
        /// <param name="filePath">文件路径</param>
        /// <returns></returns>
        private async Task<string> UploadFile(CosCloud cos, string filePath)
        {
            return await Task.Run(()=>
            {               
                if (cos == null)
                {
                    return string.Empty;
                }
                var fileName = string.Format("{0}/{1}", remotePath, Path.GetFileName(filePath));

                return cos.UploadFile(bucketName, fileName, filePath);
            });
            
        }

        /// <summary>
        /// 生成Token
        /// </summary>
        /// <param name="username">用户名</param>
        /// <param name="password">密码</param>
        /// <returns>
        /// 返回Json数据,例:
        /// 成功:
        /// {
        /// "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiYWJ5IiwianRpIjoiMTdkZjUyODgtMzllMS00MjIyLTlkYWUtOWRjMGVkMTU3OTUyIiwiZXhwIjoxNTA4MzgzMDg1LCJpc3MiOiJiYWJ5IiwiYXVkIjoiYmFieSJ9.hdeNip5lw9lJtPMJHEj988zqa32TH4nTPg8vhgRKGUM",
        /// "msg": "创建Token成功",
        /// "code": 0
        /// }
        /// 失败:
        ///{
        ///    "msg": "用户不存在",
        ///    "code": 1
        /// }
        /// </returns>
        [Route("api/GenerateToken")]
        [AllowAnonymous]
        [HttpPost]
        public async Task<IActionResult> GenerateToken(string username, string password)
        {
            /*
             错误代码:
             自定义代码0-99
             0:成功
             1:用户不存在
             2:密码错误
             3:用户被锁定
             */
            string Message = "";
            int ErrCode = 0;
            try
            {
                var usr = await _repo.Users.IgnoreQueryFilters().FirstOrDefaultAsync(w => w.UserName == username);
                if (usr == null)
                {
                    Message = "用户不存在";
                    ErrCode = 1;
                    return Json(new FailResult(ErrCode, Message));
                }
                else
                {

                    if (!usr.IsEnable)
                    {
                        Message = "账户被锁定,请联系管理员解锁";
                        ErrCode = 3;
                        return Json(new FailResult(ErrCode, Message));
                    }
                    if (LoginHelper.VerifyHashedPassword(password, usr.PasswdHash, usr.Salt))
                    {

                        usr.FailRetry = 0;
                        usr.LastLoginTime = DateTime.Now;
                        usr.LastLoginIP = Request.HttpContext.Connection.RemoteIpAddress.ToString();
                        _repo.Update(usr);
                        _repo.SaveChanges();

                        var claims = new[]
                         {
                          new Claim(JwtRegisteredClaimNames.Sub, usr.UserName),
                          new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                         };


                        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JwtBearer:SecurityKey"]));
                        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                        var expire = DateTime.Now.AddSeconds(_expiresSpan.TotalSeconds);
                        var token = new JwtSecurityToken(_config["JwtBearer:Issuer"],
                          _config["JwtBearer:Issuer"],
                          claims,
                          expires: expire,
                          signingCredentials: creds);
                        var strToken = new JwtSecurityTokenHandler().WriteToken(token);
                        _logger.LogTrace("IP为{0}用户(User:{1})获取Token:{2}", ServerHelper.GetIP(HttpContext), username,strToken);
                        return Json(new CreateTokenResult(username, strToken) { Expires = expire});
                    }
                    else
                    {
                        Message = "密码错误";
                        var failCount = 0;
                        int.TryParse(_config["user:MaxFailCount"], out failCount);
                        if (usr.FailRetry < failCount)
                        {
                            usr.FailRetry += 1;
                            ErrCode = 2;
                        }
                        else
                        {
                            usr.IsEnable = false;
                            Message = "密码输入错误次数太多,已被锁定";
                            ErrCode = 3;
                        }
                        _repo.Update(usr);
                        _repo.SaveChanges();
                    }
                }

            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "生成Token异常");
                ErrCode = 500;
                Message = "服务器内部异常";
            }


            return Json(new FailResult(ErrCode, Message));
        }


        [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
        [Route("api/UploadAlbumsInfo")]
        [HttpPost]
        public JsonResult UploadAlbumsInfo([FromBody] UploadAlbumsParam albumInfo)
        {
             int nSucceedFile = 0;
            if(albumInfo == null)
            {
                return Json(new FailResult(1001, "参数不能为空"));
            }
            var param = albumInfo;//JsonConvert.DeserializeObject < UploadAlbumsParam>(albumInfo);
            if(param == null)
            {
                return Json(new FailResult(2001, "无法解析参数"));
            }
            try
            {
                var json = JsonConvert.SerializeObject(albumInfo);
                Console.WriteLine(json);

                int clientType = 1;
                
                var albumName = param.AlbumName;
                var userName = param.UserName;
                
                var isPublic = param.IsPublic;
                if (param.Photos ==null || param.Photos.Count < 1)
                {
                    return Json(new FailResult(1001, "请提供上传文件"));
                }
                if (String.IsNullOrEmpty(albumName))
                {
                    return Json(new FailResult(1002, "请指定相册名称"));
                }
                var userEmpty = false;
                var strAlbumType = "public";
                IQueryable<AlbumInfoModel> query = null;
                if (string.IsNullOrEmpty(userName))
                {
                     query = _repo.Albums.Where(a => a.Name == albumName && a.IsPublic && a.Owner == string.Empty);
                    userEmpty = true;
                    strAlbumType = "public";
                }
                else
                {
                    query = _repo.Albums.Where(a => a.Name == albumName && a.Owner == userName && a.IsPublic == isPublic);
                    userEmpty = false;
                    strAlbumType = "user";
                }
                
                var album = query.FirstOrDefault();
                string albumPath = "";
               
                if (album == null) //不存在的相册则进行创建
                {                    
                    if (userEmpty) //公共目录
                    {
                        albumPath = Path.Combine(_environment.WebRootPath, "albums",strAlbumType,albumName);
                    }
                    else
                    {
                        albumPath = Path.Combine(_environment.WebRootPath, "albums", strAlbumType,userName, albumName);
                    }

                                       
                    var alumId = Guid.NewGuid().ToString();
                    album = new AlbumInfoModel() { Id = alumId, AlbumPath = albumPath, Name = albumName, Owner = userName, CreateDate = DateTime.Now, Description = albumName, IsPublic = isPublic, AlbumType = userEmpty ? 0 : 1 };
                   
                    _repo.Albums.Add(album);
                }
                else
                {
                    albumPath = album.AlbumPath;
                }

                if (!Directory.Exists(albumPath))
                {
                    Directory.CreateDirectory(albumPath);
                }
                

                var photoQuery = _repo.Photos.Where(w=>w.AlbumName == albumName);
                // var cos = new CosCloud(APP_ID, SECRET_ID, SECRET_KEY, 60);
                StringBuilder strBuf = new StringBuilder();
                foreach (var p in param.Photos.Where(f => Utils.ServerHelper.IsImageFile(f.PhotoName)))
                {
                    var bExist = photoQuery.Any(w => w.Hash == p.FileHash);
                    //查询图片是否存在数据库中
                    if (!bExist)
                    {
                        //不存在则添加
                        var photo = new PhotoInfoModel()
                        {
                            AlbumName = albumName,
                            Description = p.PhotoName,
                            Hash = p.FileHash,
                            LocalPath = "",
                            Name = p.PhotoName,
                            UploadDate = DateTime.Now,
                            UploadUser = userName,
                            UploadClient = clientType,
                            Path = p.Path,
                            CDNUrl = p.CDNUrl,
                            CDNUrl2 = p.CDNUrl2
                        };
                        strBuf.Append(p.PhotoName + ",");
                        _repo.Photos.Add(photo);
#if DEBUG
                        Console.WriteLine("添加记录:" + photo.Hash);
#endif
                        ++nSucceedFile;
                    }
                  
                }
                _logger.LogInformation("IP:{0}客户端更新图片文件到[{1}]目录,数量:{2},成功", ServerHelper.GetIP(HttpContext),albumName,param.Photos.Count);
                album.CoverPhoto = photoQuery.FirstOrDefault()?.CDNUrl;
                try
                {                    
                    
                    var nRet = _repo.SaveChanges();
                    _logger.LogInformation("客户端上传写入成功记录数:" + nRet.ToString());
#if DEBUG
                    Console.WriteLine("客户端上传写入成功记录数:" + nRet.ToString());
#endif
                    //删除缓存
                    _cache.Remove(CacheKeys.AllAlbumsKey);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "上传写数据库失败，user:{0},albums{1},photos:[2]", userName, albumName, strBuf.ToString());
                    return Json(new FailResult(300100, ex.Message));
                }
               
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "上传文件失败,相册名称:{0},错误信息:{1}", param.AlbumName, ex.Message);
                return Json(new FailResult(500, ex.Message));
            }

            return Json(new SucceedResult() { Msg = "更新上传图片成功："+nSucceedFile});            
        }
    }
}
